#include"XmlHandler.h" 

/*!
\file XmlHandler.cpp
\brief Implementation of XmlHandler class.
\ingroup Graph
*/

XmlHandler::XmlHandler(){
	printf("Created handler.\n");
	m_graph = 0;
	m_constructionInProgress = false;
}

XmlHandler::~XmlHandler(){
	printf("Deleted handler.\n");
}

bool XmlHandler::startDocument(){
	printf( "startDocument\n" );
	return true;
}

bool XmlHandler::endDocument(){
	printf( "endDocument\n" );
	return true;
}

bool XmlHandler::startElement( const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts ){
	printf( "startElement(%s, %s, %s)\n", namespaceURI.toAscii().data(), localName.toAscii().data(), qName.toAscii().data() );
	
	if( localName == "graph" ){
		return createGraph(atts);	
	}else if( localName == "node" ){
		return createNode(atts);
	}else if( localName == "edge" ){
		return createEdge(atts);
	}else{
		m_error = QString("Element type ").append(localName).append(" not known.");
		return false;
	}
	
	return true;
}

bool XmlHandler::createGraph( const QXmlAttributes &atts ){
	if( m_graph != 0 ){//Error, this would be the 2nd graph to be defined
		m_error = QString( "Attempted to create more than one graph." );
		return false;
	}
	
	if( atts.count() > 1 ){
		m_error = QString( "More than one attirubte specified for graph." );
		return false;
	}else if( atts.count() == 0 )
		m_graph = new Graph;
	else{
		if(atts.localName( 0 ) != "directed"){
			m_error = QString("Attibute type ").append(atts.localName(0)).append(" for graph not known.");
			return false;
		}
		if( atts.value(0) == "false" || atts.value(0) == "0" )
			m_graph = new Graph(false);
		else
			m_graph = new Graph(true);
	}
	m_constructionInProgress = true;
	return true;
}

bool XmlHandler::createNode( const QXmlAttributes &atts ){
	if( !m_constructionInProgress ){
		m_error = QString("Node outside graph definition.");
		return false;
	}
	if( atts.count() > 2 ){
		m_error = QString("More than two attributes specified for node.");
		return false;
	}
	QString label = QString("");
	int id = -1;
	
	for( int i = 0; i < atts.count(); i++ ){
		if( atts.localName(i) == "identifier" || atts.localName(i) == "id" ){
			if(!parseInt(atts, i, id))
				return false;
		}else if( atts.localName(i) == "label" ){
			label = QString("\"").append(atts.value(i)).append(QString("\""));
			printf( "Read node label input: %s\n", atts.value(i).toAscii().data() );
		}else{
			m_error = QString( "Unknown attribute for node " ).append(atts.localName(i));
			return false;
		}
	}
	
	m_graph->createNode( label, id );
	return true;
}

bool XmlHandler::createEdge( const QXmlAttributes &atts ){
	if( !m_constructionInProgress ){
		m_error = QString( "Edge outside graph definition." );
		return false;
	}
	if( atts.count() > 4 ){
		m_error = QString("More than four attributes specified for node.");
		return false;
	}
	
	QString label = QString("");
	int id = -1;
	int nodeFromId = -1;
	int nodeToId = -1;
	
	for( int i = 0; i < atts.count(); i++ ){
		if( atts.localName(i) == "from" ){
			if(!parseInt(atts, i, nodeFromId))
				return false;
		}else if( atts.localName(i) == "to" ){
			if(!parseInt(atts, i, nodeToId))
				return false;
		}else if( atts.localName(i) == "label" ){
			label = QString("\"").append(atts.value(i)).append(QString("\""));
		}else if( atts.localName(i) == "id" || atts.localName(i) == "identifier" ){
			if(!parseInt(atts, i, id))
				return false;
		}else{
			m_error = QString( "Unknown attribute for edge " ).append(atts.localName(i));
			return false;
		}
	}
	
	bool ok = true;
	const Node &nodeFrom = m_graph->nodeWithId(nodeFromId, &ok);
	if(!ok){
		m_error = QString("Source node not specified or existing for edge.");
		return false;
	}
	const Node &nodeTo = m_graph->nodeWithId(nodeToId, &ok);
	if(!ok){
		m_error = QString("Destination node not specified or existing for edge.");
		return false;
	}
	
	m_graph->createEdge( nodeFrom, nodeTo, label, id );
	
	return true;
}

bool XmlHandler::parseInt( const QXmlAttributes &atts, int i, int &ret ){
	bool ok = true;
	ret = atts.value(i).toInt(&ok);
	if(!ok){
		m_error = QString( "Could not parse " ).append(atts.localName(i)).append(" = \"").append(atts.value(i)).append("\"");
		return false;
	}
	return true;
}

bool XmlHandler::endElement( const QString &namespaceURI, const QString &localName, const QString &qName ){
	printf( "endElement(%s, %s, %s)\n", namespaceURI.toAscii().data(), localName.toAscii().data(), qName.toAscii().data() );
	if( localName == "graph" )
		m_constructionInProgress = false;
	return true;
}

bool XmlHandler::startEntity( const QString &name ){
	printf("startEntity(%s)\n", name.toAscii().data());
	return true;
}

bool XmlHandler::endEntity( const QString &name ){
	printf("endEntity(%s)\n", name.toAscii().data());
	return true;
}

bool XmlHandler::comment( const QString &ch ){
	printf( "comment(%s)\n", ch.toAscii().data() );
	return true;
}

bool XmlHandler::startDTD( const QString &/*name*/, const QString &/*publicID*/, const QString &/*sysID*/ ){
	printf("startDTD()\n");
	return true;
}

bool XmlHandler::endDTD(){
	printf("endDTD()\n");
	return true;
}

Graph *XmlHandler::fromFile( const QString &fileName, QString &err ){
	QXmlSimpleReader *reader = new QXmlSimpleReader;
	
	QFile *file = new QFile( fileName );
	if( !file->open(QIODevice::ReadOnly) ){
		printf("Could not open file.\n");
		return 0;
	}
	QXmlInputSource *source = new QXmlInputSource( file );
	XmlHandler *handler = new XmlHandler;
	
	reader->setContentHandler( handler );
	reader->setErrorHandler( handler );
	
	bool ok = reader->parse( source );
	
	delete source;
	delete file;
	delete reader;
	
	if( !ok ){
		err = handler->error();
		handler->deleteGraph();
		delete handler;
		return 0;
	}
	
	Graph *ret = handler->graph();
	delete handler;
	return ret;
}
